import { Typer, TyperPage, TyperChunk, TyperFlow, TyperFlowStep, TyperFlowAction } from '../interface/types/typer'
import { isEmpty, isNotEmpty } from '../kit/validate'
import { appModule } from '../store/modules/app'

class TyperKit implements Typer {
  private chunkArray: Array<TyperChunk> = []
  private chunkIndexStorer: Map<string, number> = new Map<string, number>() // chunkKey: chunkIndex
  private pageArray: TyperPage[] = []
  private pageIndexStorer: Map<string, number> = new Map<string, number>() // routeName: pageIndex
  private flowArray: TyperFlow[] = []
  private flowIndexStorer: Map<string, number> = new Map<string, number>() // flowId: flowIndex
  private stepStorer: Map<string, TyperFlowStep> = new Map<string, TyperFlowStep>() // flowId-routeName: TyperFlowStep
  private actionStorer: Map<string, TyperFlowAction> = new Map<string, TyperFlowAction>() // flowId-routeName-id: TyperFlowAction

  public initTyper(chunks: TyperChunk[], pages: TyperPage[], flows?: TyperFlow[]) {
    this.clearStorer()
    if (isNotEmpty(chunks)) {
      chunks.forEach(chunk => {
        this.addChunk(chunk)
      })
    }
    if (isNotEmpty(pages)) {
      pages.forEach(page => {
        this.addPage(page)
      })
    }
    if (isNotEmpty(flows)) {
      flows?.forEach(flow => {
        this.addFlow(flow)
      })
    }
    appModule.setTyperInitialized(true)
  }

  private clearStorer() {
    this.chunkArray = []
    this.chunkIndexStorer.clear()
    this.pageArray = []
    this.pageIndexStorer.clear()
    this.flowArray = []
    this.flowIndexStorer.clear()
    this.stepStorer.clear()
    this.actionStorer.clear()
  }

  private addChunk(chunk: TyperChunk) {
    const chunkKey: string = chunk.chunkKey
    this.chunkArray.push(chunk)
    this.chunkIndexStorer.set(chunkKey, this.chunkArray.length - 1)
  }

  private addPage(page: TyperPage) {
    const routeName: string = page.routeName
    this.pageArray.push(page)
    this.pageIndexStorer.set(routeName, this.pageArray.length - 1)
  }

  private buildStepKey(flowId: string, routeName: string) {
    return flowId + '-' + routeName
  }

  private buildActionKey(flowId: string, routeName: string, id: string) {
    return flowId + '-' + routeName + '-' + id
  }

  private addFlow(flow: TyperFlow) {
    const flowId = flow.id
    const steps = flow.steps
    if (isNotEmpty(steps)) {
      steps.forEach(step => {
        const routeName = step.routeName
        const actions = step.actions
        if (isNotEmpty(actions)) {
          actions.forEach(action => {
            const id = action.id
            const actionKey = this.buildActionKey(flowId, routeName, id)
            this.actionStorer.set(actionKey, action)
          })
        }
        const stepKey = this.buildStepKey(flowId, routeName)
        this.stepStorer.set(stepKey, step)
      })
    }
    this.flowArray.push(flow)
    this.flowIndexStorer.set(flowId, this.flowArray.length - 1)
  }

  public getChunk(chunkKey: string) {
    const chunkIndex = this.chunkIndexStorer.get(chunkKey)
    if (isNotEmpty(chunkIndex)) {
      return this.chunkArray[chunkIndex as number]
    }
    return null
  }

  public getChunks(routeName?: string) {
    if (isEmpty(routeName)) {
      return this.chunkArray
    } else {
      const page = this.getPage(routeName || '')
      const chunks: TyperChunk[] = []
      if (isNotEmpty(page)) {
        const chunkKeys = page?.chunkKeys || []
        chunkKeys.forEach(chunkKey => {
          const chunk = this.getChunk(chunkKey)
          if (isNotEmpty(chunk)) {
            chunks.push(chunk as TyperChunk)
          }
        })
      }
      return chunks
    }
  }

  public getPage(routeName: string) {
    const pageIndex = this.pageIndexStorer.get(routeName)
    if (isNotEmpty(pageIndex)) {
      return this.pageArray[pageIndex as number]
    }
    return null
  }

  public getPages(flowId?: string) {
    if (isEmpty(flowId)) {
      return this.pageArray
    } else {
      const flow = this.getFlow(flowId || '')
      const pages: TyperPage[] = []
      if (isNotEmpty(flow)) {
        const steps = flow?.steps || []
        steps.forEach(step => {
          const page = this.getPage(step.routeName)
          if (isNotEmpty(page)) {
            pages.push(page as TyperPage)
          }
        })
      }
      return pages
    }
  }

  public getFlow(flowId: string) {
    const flowIndex = this.pageIndexStorer.get(flowId)
    if (isNotEmpty(flowIndex)) {
      return this.flowArray[flowIndex as number]
    }
    return null
  }

  public getFlows() {
    return this.flowArray
  }

  public getStep(flowId: string, routeName: string) {
    const stepKey = this.buildStepKey(flowId, routeName)
    const step = this.stepStorer.get(stepKey)
    if (isNotEmpty(step)) {
      return step as TyperFlowStep
    }
    return null
  }

  public getSteps(flowId: string) {
    const flow = this.getFlow(flowId)
    let steps: TyperFlowStep[] = []
    if (isNotEmpty(flow)) {
      steps = flow?.steps || []
    }
    return steps
  }

  public getAction(flowId: string, routeName: string, id: string) {
    const actionKey = this.buildActionKey(flowId, routeName, id)
    const action = this.actionStorer.get(actionKey)
    if (isNotEmpty(action)) {
      return action as TyperFlowAction
    }
    return null
  }

  public getActions(flowId: string, routeName: string) {
    const step = this.getStep(flowId, routeName)
    return step?.actions || []
  }
}

const typerKit = new TyperKit()

export default typerKit
